package com.burt.framework.servlet;

import com.alibaba.fastjson.JSON;
import com.burt.aop.LogInterceptor;
import com.burt.framework.annotation.BurtController;
import com.burt.framework.annotation.BurtRequestMapping;
import com.burt.framework.annotation.BurtRequestParam;
import com.burt.framework.annotation.BurtResponseBody;
import com.burt.framework.context.BurtApplicationContext;
import com.burt.mvc.controller.TestController;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Description:DispatcherServlet
 * User: Burt
 * Date: 2017-06-22
 * Time: 22:26
 */
public class BurtDispatcherServlet extends HttpServlet {

    //web.xml -> servlet -> init-param -> param-name
    private static final String LOCATION = "contextConfigLocation";

    private List<Handler> handlerMapping = new ArrayList<Handler>();

    private Map<Handler,HandlerAdapter> adapterMapping = new HashMap<Handler, HandlerAdapter>();

    @Override
    public void init(ServletConfig config) throws ServletException {

        //初始化IOC容器
        BurtApplicationContext context = new BurtApplicationContext(config.getInitParameter(LOCATION));

        //请求解析
        initMultipartResolver(context);
        //多语言、国际化
        initLocaleResolver(context);
        //主题View层的
        initThemeResolver(context);

        //============== 重要 ================
        //解析url和Method的关联关系
        initHandlerMappings(context);
        //适配器（匹配的过程）
        initHandlerAdapters(context);
        //============== 重要 ================

        //异常解析
        initHandlerExceptionResolvers(context);
        //视图转发（根据视图名字匹配到一个具体模板）
        initRequestToViewNameTranslator(context);

        //解析模板中的内容（拿到服务器传过来的数据，生成HTML代码）
        initViewResolvers(context);

        initFlashMapManager(context);

        System.out.println(config.getServletName() + " is init.");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            doDispatch(req,resp);
        } catch (Exception e) {
            e.printStackTrace();
            resp.getWriter().write("500 Exception, Msg : " + Arrays.toString(e.getStackTrace()));
        }
    }

    private Handler getHandler(HttpServletRequest req){
        if (handlerMapping.isEmpty()){
            return null;
        }

        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replace(contextPath, "").replaceAll("/+", "/");

        for (Handler handler : handlerMapping) {
            Matcher matcher = handler.pattern.matcher(url);
            if (!matcher.matches()){
                continue;
            }
            return handler;
        }

        return null;
    }

    private HandlerAdapter getHandlerAdapter(Handler handler){
        if (adapterMapping.isEmpty()){
            return null;
        }
        return adapterMapping.get(handler);
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception{

        try {
            Handler handler = getHandler(req);
            if (null == handler){
                resp.getWriter().write("404 Not Found.");
                return;
            }

            HandlerAdapter ha = getHandlerAdapter(handler);

            BurtModelAndView mv = ha.handle(req, resp, handler);

            //判断是被调用的方法上是否有@BurtResponseBody注解
            boolean isBurtResponseBody = handler.method.isAnnotationPresent(BurtResponseBody.class);
            if(isBurtResponseBody){
                //如果有@BurtResponseBody则转成json
                String jsonString = JSON.toJSONString(mv);
                resp.getWriter().write(jsonString);
            } else {
                //TODO 处理没有@BurtResponseBody注解的返回
            }
        } catch (Exception e){
            throw e;
        }

    }

    /**
     * 请求解析
     * @param context
     */
    private void initMultipartResolver(BurtApplicationContext context) {
    }

    /**
     * 多语言、国际化
     * @param context
     */
    private void initLocaleResolver(BurtApplicationContext context) {
    }

    /**
     * 主题
     * @param context
     */
    private void initThemeResolver(BurtApplicationContext context) {
    }

    /**
     * 解析url和Method的关联关系
     * @param context
     */
    private void initHandlerMappings(BurtApplicationContext context) {
        Map<String,Object> ioc = context.getAll();
        if (ioc.isEmpty()){
            return;
        }

        //找出有@BurtController注解的类的所有方法
        //而且这个方法上应该要加了RequestMaping注解才能被外界来访问
        for (Map.Entry<String,Object> entry : ioc.entrySet()){
            Class<?> clazz = entry.getValue().getClass();
            //因为在IOC容器的初如化 doCreateBean 中对controller使用了cglib代理(日志)，所以这里要getSuperclass()
            Class<?> superclass = clazz.getSuperclass();
            if (!superclass.isAnnotationPresent(BurtController.class)){
                continue;
            }

            String url = "";
            //写在类上的@RequestMapping
            if (superclass.isAnnotationPresent(BurtRequestMapping.class)){
                BurtRequestMapping requestMapping = superclass.getAnnotation(BurtRequestMapping.class);
                url = requestMapping.value();
            }

            //扫描Controller下面的所有方法
            Method[] methods = superclass.getMethods();
            for (Method method : methods) {
                if (!method.isAnnotationPresent(BurtRequestMapping.class)){
                    continue;
                }

                BurtRequestMapping requestMapping = method.getAnnotation(BurtRequestMapping.class);
                //把多个/替换成一个/
                String regex = (url + requestMapping.value()).replaceAll("/+", "/");
                //将给定的正则表达式编译并赋予给Pattern类
                Pattern pattern = Pattern.compile(regex);
                handlerMapping.add(new Handler(pattern,entry.getValue(),method));

                System.out.println("Mapping: " + regex + " " + method.toString());
            }
        }
    }

    /**
     * 适配器（匹配的过程）,主要是用来动态匹配我们参数的
     * @param context
     */
    private void initHandlerAdapters(BurtApplicationContext context) {
        if (handlerMapping.isEmpty()){
            return;
        }
        //参数类型作为key，参数的索引作为值
        Map<String,Integer> paramMapping = new HashMap<String, Integer>();

        //只需取出具体的某个方法
        for (Handler handler : handlerMapping) {
            //获取该方法的年有参数
            Class<?>[] paramsTypes = handler.method.getParameterTypes();

            //参数有顺序，通过反射不能拿到参数名字
            for (int i = 0; i < paramsTypes.length; i++){
                Class<?> type = paramsTypes[i];
                //匹配Request和Response
                if (HttpServletRequest.class == type || HttpServletResponse.class == type){
                    paramMapping.put(type.getName(), i);
                }
            }

            Annotation[][] pa = handler.method.getParameterAnnotations();
            for (int i = 0; i < pa.length; i++){
                for (Annotation a : pa[i]){
                    if (a instanceof BurtRequestParam){
                        String paramName = ((BurtRequestParam) a).value();
                        if (!"".equals(paramName.trim())){
                            paramMapping.put(paramName,i);
                        }
                    }
                }
            }
            adapterMapping.put(handler, new HandlerAdapter(paramMapping));
        }
    }

    /**
     * 异常解析
     * @param context
     */
    private void initHandlerExceptionResolvers(BurtApplicationContext context) {
    }

    /**
     * 视图转发（根据视图名字匹配到一个具体模板）
     * @param context
     */
    private void initRequestToViewNameTranslator(BurtApplicationContext context) {
    }

    /**
     * 解析模板中的内容（拿到服务器传过来的数据，生成HTML代码）
     * @param context
     */
    private void initViewResolvers(BurtApplicationContext context) {
    }

    /**
     * flash映射管理器
     * @param context
     */
    private void initFlashMapManager(BurtApplicationContext context) {
    }

    /**
     * 定义 HandlerMapping
     */
    private class Handler{

        protected Object controller;

        protected Method method;

        protected Pattern pattern;

        private Handler(Pattern pattern,Object controller,Method method){
            this.pattern = pattern;
            this.controller = controller;
            this.method = method;
        }

        @Override
        public String toString() {
            return "Handler{" +
                    "controller=" + controller +
                    ", method=" + method +
                    ", pattern=" + pattern +
                    '}';
        }
    }

    /**
     * 方法适配器
     */
    private class HandlerAdapter{

        private Map<String,Integer> paramMapping;

        public HandlerAdapter(Map<String, Integer> paramMapping) {
            this.paramMapping = paramMapping;
        }

        public BurtModelAndView handle(HttpServletRequest req, HttpServletResponse resp, Handler handler) throws Exception{

            Class<?>[] paramTypes = handler.method.getParameterTypes();

            //只能通过索引号来找到具体的某个参数并给参数赋值
            Object[] paramValues = new Object[paramTypes.length];

            Map<String, String[]> params = req.getParameterMap();

            for (Map.Entry<String, String[]> param : params.entrySet()) {
                String value = Arrays.toString(param.getValue())
                        .replaceAll("\\[|\\]", "")
                            .replaceAll(",\\s", ",");
                if (!this.paramMapping.containsKey(param.getKey())){
                    continue;
                }

                int index = this.paramMapping.get(param.getKey());
                //单个赋值是不行的
                paramValues[index] = castStringValue(value,paramTypes[index]);
            }

            //为request response赋值
            String reqName = HttpServletRequest.class.getName();
            if (this.paramMapping.containsKey(reqName)){
                int reqIndex = this.paramMapping.get(reqName);
                paramValues[reqIndex] = req;
            }
            String respName = HttpServletResponse.class.getName();
            if (this.paramMapping.containsKey(respName)){
                int respIndex = this.paramMapping.get(respName);
                paramValues[respIndex] = resp;
            }

            boolean isModelView = BurtModelAndView.class == handler.method.getReturnType();
            Object r = handler.method.invoke(handler.controller, paramValues);
            if (isModelView){
                return (BurtModelAndView) r;
            } else {
                return null;
            }

        }

    }

    private Object castStringValue(String value, Class<?> paramType) {
        if (String.class == paramType){
            return value;
        }else if (Integer.class == paramType){
            return Integer.valueOf(value);
        }else if (int.class == paramType){
            return Integer.valueOf(value).intValue();
        }
        return null;
    }
}
